package il.ac.technion.cs236700.address_book;

import il.ac.technion.cs236700.utils.Misc;
import il.ac.technion.cs236700.utils.CSV.Separator;
import il.ac.technion.cs236700.utils.DBC.DBC;
import il.ac.technion.cs236700.utils.annotations.Implementor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** 
 * 
 * @authors Regev Azriel & Sharon Ronen
 *
 */


@Implementor
public class AddressBookImpl implements AddressBook {

	private ArrayList<Contact> contacts = new ArrayList<Contact>();
	private ArrayList<String> outputFields = new ArrayList<String>();
	private ArrayList<String> supportedKeys = new ArrayList<String>();

	public AddressBookImpl() {
		supportedKeys.addAll(Defaults.SUPPORTED_KEYS);
		outputFields.addAll(getSupportedKeysList());
	}

	public void add(Person p, Address home, Address work) {
		final Map<String,String> values = new HashMap<String,String>();
		if(p != null) {
			values.put("First Name", p.firstName);
			values.put("Last Name", p.lastName);
			values.put("Title", p.title);
		}	
		if(home != null)
			values.put("Home Address", home.toString());
		if (work != null)
			values.put("Work Address", work.toString());
		contacts.add(new Contact(values));
	}

	public void add(Map<String, String> map) {
		contacts.add(new Contact(map));
	}

	public String getConflicts() {		
		String $ = Separator.separateBy(outputFields, ",")+"\n";
		String $$ = "";
		ArrayList<Contact> conflicts = new ArrayList<Contact>();
		for (Contact c1 : contacts) {
			for (Contact c2 : contacts) {
				if (!conflicts.contains(c1) && c1.conflictsWith(c2)){
					conflicts.add(c1);
					$$+=c1.output(outputFields)+"\n";
				}
			}
		}
		return $$.isEmpty()?"":$+$$;
	}

	public String getNormalized() {
		DBC.require(getConflicts().isEmpty(), 
				"AddressBook contains conflicts:\n ",getConflicts());
		String $ = new String(Separator.separateBy(outputFields, ",")+"\n");
		ArrayList<Contact> contacts_copy = Misc.cloneArray(contacts);
		for (Contact c1 : contacts_copy){
			for (Contact c2 : contacts_copy) {
				if (c1.canUnify(c2)){					
					c1.unify(c2);
				}
			}
		}	
		for (Object c : Misc.distinct(contacts_copy.toArray())) {
			DBC.require(c instanceof Contact, "");
			$+=((Contact)c).output(outputFields)+"\n";
		}
		return $;
	}

	public String[] getSupportedKeys() {
		return supportedKeys.toArray(new String[supportedKeys.size()]);
	}
	


	public void load(String csv) {
		if (csv == null){
			return;
		}
		String[] $ = csv.split("\n");
		String[] firstLine = $[0].split(",");
		for (int i = 1; i < $.length; i++) {
			final Map<String,String> values = new HashMap<String,String>();
			String[] $_ = $[i].split(",");
			for (int j  = 0; j <$_.length; j++) {
				if ($_[j] != ""){
					values.put(firstLine[j],$_[j]);
				}
			}
			contacts.add(new Contact(values));
		}
	}

	public void setOutput(String... keys) {
		List<String> l = Arrays.asList(keys);
		DBC.require(supportedKeys.containsAll(l),
		"Some keys are not supported");
		outputFields.clear();
		outputFields.addAll(l);
	}

	public String[] getOutputFields() {
		return outputFields.toArray(new String[outputFields.size()]);
	}

	public ArrayList<Contact> getContactsCopy() {
		return Misc.cloneArray(contacts);		
	}
	
	public boolean exists(final Person p)
	{ 
		return find(p)!=null;
	}
	
	public Contact getContact(final Person p){
		return find(p);
	}
	
	public Contact find(Person p) {
		for (Contact contact : contacts)
			if (contact.contains(p)) return contact;
		return null;
	}	
	
	public ArrayList<Contact> getContacts(){
		return contacts;
	}
	
	protected List<String> getOutputFieldsArray(){
		return outputFields;
	}
	protected void setContacts(ArrayList<Contact> contacts){
		this.contacts = contacts;
	}

	public ArrayList<String> getSupportedKeysList() {
		return supportedKeys;
	}
}
